import 'dart:async';
import 'dart:io';
import 'package:background_downloader/background_downloader.dart';
import 'package:get/get.dart';
import 'package:get_demo/pages/download/presentation/controllers/MapDataController.dart';
import 'package:uuid/uuid.dart';

import '../../domain/entity/DownloadData.dart';

class DownloadProvider extends GetConnect {
  final String TAG = "DownloadController";
  List<DownloadTask> _tasks = [];
  DownloadData? data = null;
  bool _isThreadStop = true;
  Rx<DownloadData?> dataRx = Rx(null);

  DownloadProvider() {
    _init();
  }

  void _init() {
    print("Init listen");
    FileDownloader().updates.listen((update) {
      switch (update) {
        case TaskStatusUpdate():
          switch (update.status) {
            case TaskStatus.complete:
              {
                // print('Task ${update.task.taskId} success!');
                _onFileFinish(update.task.taskId, true);
              }

            case TaskStatus.canceled:
              print('Download was canceled');

            case TaskStatus.paused:
              print('Download was paused');

            case TaskStatus.failed:
              {
                // print('Download was failed:${update.exception}');
                _onFileFinish(update.task.taskId, false);
              }
            default:
            // print(
            //     'Task ${update.task.taskId} ${update.status} Download not successful');
          }

        case TaskProgressUpdate():
        // print('progress:${update.progress}');
        // process the TaskProgressUpdate, e.g.
        // progressUpdateStream.add(update); // pass on to widget for indicator
      }
    });
  }

  bool _isFinish() {
    return data?.status != MapTaskStatus.DOWNING;
  }

  void _onFileFinish(String taskId, bool suc) {
    print("task:$taskId suc:$suc");
    _increment(taskId, suc);
    var task = null;
    for (var element in _tasks) {
      if (element.taskId == taskId) {
        task = element;
      }
    }
    _tasks.remove(task);
    if (_tasks.isEmpty && _isFinish()) {
      var cur = DateTime.now().millisecondsSinceEpoch;
      var start = data!.startTime;
      data!.endTime = cur;
      print("所有下载任务执行完成 cast ${cur - start}");
      updateView();
    }

    // if (!_isFinish()) {
    //   _processTasks();
    // }
  }

  updateView() {
    if (data != null) {
      dataRx.value = DownloadData(
        layers: data!.layers,
        params: data!.params,
        status: data!.status,
        z: data!.z,
        x: data!.x,
        y: data!.y,
        total: data!.total,
        downSuc: data!.downSuc,
        downFailt: data!.downFailt,
      );
    }
  }

  /**
   * 开始地图瓦片下载任务
   * @params 下载参数
   * list 瓦片各层级 z，x，y的下载区域  
   */
  Future<String> startMapDownloadTask(
      (MapDownloadParams, List<LayerBean>) params) async {
    var mapParams = params.$1;
    var list = params.$2;
    data ?? (data = DownloadData(layers: list, params: mapParams));
    if (data?.status == MapTaskStatus.DOWNING) {
      print('当前有下载任务在执行');
      return Future.value("当前有下载任务在执行");
      // throw (Exception('当前有下载任务在执行'));
    }
    if (list.isEmpty) {
      return Future.value("下载任务为空");
    }
    print('startMapDownloadTask');
    _initTaskData(list, mapParams);
    _tasks.clear();
    await _processTasks();
    return Future.value("");
  }

  void puaseTask() {
    print('暂停任务');
    data?.status = MapTaskStatus.PAUSE;
    updateView();
  }

  String resumeTask() {
    if (data == null) {
      print('任务为空');
      return "任务为空";
    }
    if (data!.status == MapTaskStatus.PAUSE) {
      print('恢复任务');
      data!.status = MapTaskStatus.DOWNING;
      _processTasks();
      return "";
    } else {
      print('任务非暂停状态');
      return "任务非暂停状态";
    }
  }

  void stopMapDownloadTask() {
    data!.status = MapTaskStatus.STOP;
    var cur = DateTime.now().millisecondsSinceEpoch;
    data!.endTime = cur;
    updateView();
  }

  void _increment(String taskId, bool suc) {
    if (suc) {
      data?.downSuc++;
    } else {
      data?.downFailt++;
    }
  }

/**
 * 并发执行下载任务，任务不超过最大线程数，
 */
  Future _processTasks() async {
    if (_isFinish()) {
      print("下载任务结束");
      return null;
    }
    if (!_isThreadStop) {
      print("下载任务进行中");
      return null;
    }
    _isThreadStop = false;
    updateView();
    while (!_isFinish()) {
      int delay = 2;
      if (_tasks.length < data!.params.maxThread) {
        List<LayerBean> _layers = data!.layers;
        int x = data!.x;
        int y = data!.y;
        int z = data!.z;
        int total = data!.total;
        int downSuc = data!.downSuc;
        int downFailt = data!.downFailt;
        print(
            "$TAG,${_tasks.length} cur: z:$z x:$x y:$y total:$total suc:$downSuc fail:$downFailt");
        var res = await startDownload(z, x, y);
        //文件需要下载，delay 100ms等待
        if (res) {
          delay = 100;
        }
        LayerBean bean = _layers.firstWhere((element) => element.zoom == z);
        LayerBean last = _layers.last;
        if (y >= bean.maxY) {
          if (x >= bean.maxX) {
            if (z >= last.zoom) {
              //下载任务结束
              data!.status = MapTaskStatus.SUCCESS;
              var cur = DateTime.now().millisecondsSinceEpoch;
              var start = data!.startTime;
              data!.endTime = cur;
              print("下载任务结束 cast ${cur - start}");
              break;
            } else {
              z++;
              LayerBean nextBean =
                  _layers.firstWhere((element) => element.zoom == z);
              x = nextBean.minX;
              y = nextBean.minY;
            }
          } else {
            x++;
            y = bean.minY;
          }
        } else {
          y++;
        }
        data!.x = x;
        data!.y = y;
        data!.z = z;
      }
      updateView();
      await Future.delayed(Duration(milliseconds: delay));
    }
    _isThreadStop = true;
  }

  DownloadTask? createTask(int z, int x, int y) {
    MapDownloadParams _params = data!.params;
    String dir = _params.saveDir;
    String saveUrl = "$dir/$z/$x/$y.png";
    bool exists = File(saveUrl).existsSync();
    if (!exists) {
      return _initDownloadTask(_params.mapUrl, _params.saveDir, z, x, y);
    } else {
      return null;
    }
  }

  Future<bool> startDownload(int z, int x, int y) async {
    MapDownloadParams _params = data!.params;
    String dir = _params.saveDir;
    String saveUrl = "$dir/$z/$x/$y.png";
    bool exists = File(saveUrl).existsSync();
    if (!exists) {
      final task = _initDownloadTask(_params.mapUrl, _params.saveDir, z, x, y);
      // 添加任务到任务列表
      _tasks.add(task);
      // print("task length:${_tasks.length}");
      // FileDownloader().download(task,
      //     onProgress: (progress) => print('Progress: ${progress * 100}%'),
      //     onStatus: (status) => print('Status: $status'));
      await FileDownloader().enqueue(task);
      return Future.value(true);
      // print("task ${task} excute");
    } else {
      data!.downSuc++;
      print("文件已经下载成功");
      return Future.value(false);
    }
  }

  void _initTaskData(List<LayerBean> list, MapDownloadParams mapParams) {
    assert(data != null);
    var bean = list.first;
    data?.z = bean.zoom;
    data?.x = bean.minX;
    data?.y = bean.minY;
    data?.curLayerBean = bean;
    data?.status = MapTaskStatus.DOWNING;
    data?.downFailt = 0;
    data?.downSuc = 0;
    int total = 0;
    for (LayerBean bean in list) {
      total += (bean.maxY + 1 - bean.minY) * (bean.maxX + 1 - bean.minX);
    }
    data?.total = total;
    data?.startTime = DateTime.now().millisecondsSinceEpoch;
  }

  // void pauseDownload(String taskId) {
  //   // 更新任务状态
  //   _updateTaskStatus(taskId, DownloadTaskStatus.paused);
  // }

  // void resumeDownload(String taskId) {
  //   // 更新任务状态
  //   _updateTaskStatus(taskId, DownloadTaskStatus.running);
  // }

  // void cancelDownload(String taskId) {
  //   // 更新任务状态
  //   _updateTaskStatus(taskId, DownloadTaskStatus.canceled);
  // }

  // void _updateTaskStatus(String taskId, DownloadTaskStatus status) {
  //   var task = _tasks.firstWhere((task) => task.taskId == taskId);
  //   // task.status = status;
  //   update();
  // }

  // Future _createDir(String path) async {
  //   var dir = Directory(path);
  //   try {
  //     bool exists = dir.existsSync();
  //     if (!exists) {
  //       dir.create(recursive: true);
  //     }
  //   } catch (e) {
  //     e.printInfo();
  //   }
  // }

  DownloadTask _initDownloadTask(
      String mapUrl, String dir, int z, int x, int y) {
    String taskId = generateUUID();
    String url = mapUrl
        .replaceAll("{z}", z.toString())
        .replaceAll("{y}", y.toString())
        .replaceAll("{x}", x.toString());
    // String url = "$mapUrl/$z/$x$y.jpg";
    String saveUrl = "$dir/$z/$x/";
    String fileName = "$y.png";
    // _createDir("$dir/$z/$x/");
    return DownloadTask(
      taskId: taskId,
      url: url,
      directory: saveUrl,
      filename: fileName,
      // retries: 3,
      updates: Updates.statusAndProgress,
    );
  }

  String generateUUID() {
    var uuid = Uuid();
    return uuid.v4();
  }

  @override
  void onClose() {
    super.onClose();
    FileDownloader().updates.listen(null);
  }
}

class LayerDownloadParams {
  final String url;
  final String saveUrl;
  final int zoom;
  final int x;
  final int y;

  LayerDownloadParams(
      {required this.url,
      required this.saveUrl,
      required this.zoom,
      required this.x,
      required this.y});
}

enum DownloadTaskStatus {
  pending,
  running,
  paused,
  completed,
  canceled,
  failed,
}
